package org.example;

public class InitialOrderTest {

    public static class Parent {
        private String name;
        private int age;
        private String text = "hello";
        private String msg = "msg";

        {
            this.msg = "初始化块中设置的值";
            // 注意：afterInitialBlock定义在初始化代码块后面，因此在这里无法访问它的值
            //System.out.println(afterInitialBlock); // java: illegal forward reference

            // 但是给它赋值却是可以的
            this.afterInitialBlock = "这是一个定义在初始化代码块下方的字段";
        }

        private String afterInitialBlock = "这是一个定义在初始化代码块下方的字段";

        public Parent(){
            System.out.println("Parent的构造器中读取到name的值为：" + name); // name: null
            System.out.println("Parent的构造器中读取到age的值为：" + age);   // age: 0
            System.out.println("Parent的构造器中读取到text的值为：" + text); // text: "hello"
            System.out.println("Parent的构造器中读取到msg的值为：" + msg); // msg: "初始化块中设置的值"
            System.out.println("Parent的构造器中读取到afterInitialBlock的值为：" + afterInitialBlock); // afterInitialBlock: "这是一个定义在初始化代码块下方的字段"

            // 由于Java会保证构造器会在排初始化顺序的最后执行，所以这里可以正常访问一个定义在构造器下方的字段
            System.out.println("Parent的构造器中读取到afterConstructor的值为：" + afterConstructor); // afterConstructor: "这是一个定义在构造器下方的字段"

            // 调用一个被子类重写的方法
            this.fn();
        }

        private String afterConstructor = "这是一个定义在构造器下方的字段";

        public void fn(){
            System.out.println("parent.fn()被调用");
        }
    }

    public static class Son extends Parent {
        private String content = "world";
        private String city = "city";

        // 我很好奇为啥Java既然有了构造器，为啥还要设计实例初始化块。
        // 我觉得实例初始化块应该是为匿名内部类设计的: 普通的类中可以直接使用构造器就行，而匿名内部类由于没法定义构造器，才需要使用实例初始化块
        {
            this.city = "在子类的初始化块中设置的值";
        }

        public Son(){
            System.out.println("Son的构造器中读取到content的值为：" + content); // world
            System.out.println("Son的构造器中读取到city的值为：" + city); // 在子类的初始化块中设置的值
        }

        // 即使实例初始化块放置在构造器Son之后，它依旧是在构造器Son执行前被执行
        {
            System.out.println("执行放置在Son构造器后的实例初始化块");
        }

        @Override
        public void fn() {
            System.out.println("son.fn()被调用，content的值为：" + content);
        }
    }

    public static void main(String[] args) {
        new Son();
        // 输出：
        // Parent的构造器中读取到name的值为：null
        // Parent的构造器中读取到age的值为：0
        // Parent的构造器中读取到text的值为：hello
        // Parent的构造器中读取到msg的值为：初始化块中设置的值
        // Parent的构造器中读取到afterInitialBlock的值为：这是一个定义在初始化代码块下方的字段
        // Parent的构造器中读取到afterConstructor的值为：这是一个定义在构造器下方的字段
        // son.fn()被调用，content的值为：null注意这个！由于Son重写了fn方法，而该方法在父类的构造器中被调用，由于多态，此时调用的是子类的重写后的方法，子类的方法中访问了子类中定义content字段，然而子类定义的字段尚未被初始化，所以输出的是null而不是world）
        // 执行放置在Son构造器后的实例初始化块
        // Son的构造器中读取到content的值为：world
        // Son的构造器中读取到city的值为：在子类的初始化块中设置的值
    }
}
